<template>
  <view :class="[`mx-${props.margin[0]}`]">
    <tm-sheet
      :transprent="props.transprent !== null ? props.transprent : tmFormTransprent"
      :round="props.round"
      :margin="[0, 0]"
      :padding="props.padding"
    >
      <view :class="[`py-${props.margin[1]}`]">
        <view v-if="props.showError" style="height: 30rpx"> </view>
        <view
          :class="[
            'flex',
            tmFormLayout == 'horizontal' ? 'flex-row ' : ' ',
            tmFormLayout == 'horizontal' && !props.align ? 'flex-row-center-start' : '',
            tmFormLayout == 'vertical' && !props.align ? '' : '',
            props.align,
            props.parentClass,
          ]"
        >
          <view
            v-if="_label"
            :style="[tmFormLayout == 'horizontal'?{ width: tmFormLabelWidth + 'rpx' }:'']"
            class=" flex  flex-row"
            :class="[
              tmFormLabelAlign == 'right' ? 'flex-row-center-end' : 'flex-row-center-start',
              tmFormLayout != 'horizontal' ? 'mb-24 flex-1 ' : 'mr-32 ',
            ]"
          >
            <view style="width:12px" class="flex flex-row flex-row-center-center">
				<tm-text v-if="_required" color="red" :font-size="30" label="*"></tm-text>
			</view>
            <view class="flex flex-1 " style="width:0px">
				<tm-text
				  :color="
				    tmFormFun == 'validate' &&
				    item.isRequiredError == true &&
				    props.requiredTitleChangeColor
				      ? 'red'
				      : ''
				  "
				  
				  :font-size="30"
				  :label="_label"
				></tm-text>
			</view>
          </view>
          <view
            class="flex-1"
            :style="[tmFormLayout == 'horizontal' ? { width: '0px' } : '']"
          >
            <slot></slot>
          </view>
        </view>
        <view :class="props.desc ? 'pt-12' : ''">
          <slot name="desc">
            <tm-text color="grey-darken-2" :font-size="22" :label="props.desc"></tm-text>
          </slot>
        </view>
        <view v-if="props.showError" style="height: 30rpx">
          <tm-text
            v-if="tmFormFun == 'validate' && item.isRequiredError == true"
            color="red"
            :font-size="22"
            :label="item.message"
          ></tm-text>
        </view>
      </view>
    </tm-sheet>
    <view v-if="tmFormBorder">
      <tm-divider :border="2" :padding="[0, 0]" :margin="[0, 0]"></tm-divider>
    </view>
  </view>
</template>
<script lang="ts" setup>
/**
 * 表单项目
 * @description 只能放置在tm-from里面，不限层级。但不能嵌套使用。
 */
import {
  computed,
  watch,
  PropType,
  provide,
  ref,
  getCurrentInstance,
  onUnmounted,
  Ref,
  inject,
  isProxy,
  toRaw,
  ComputedRef,
  onMounted,
  nextTick,
} from "vue";
import tmSheet from "../tm-sheet/tm-sheet.vue";
import tmText from "../tm-text/tm-text.vue";
import tmDivider from "../tm-divider/tm-divider.vue";
import { rulesItem, inputPushItem } from "./interface";
import { formItem } from "./../tm-form/interface";
const proxy = getCurrentInstance()?.proxy ?? null;
const tmFormComnameFormItem = "tmFormComnameFormItem";
const props = defineProps({
  parentClass: {
    type: String,
    default: "",
  },
  align: {
    type: String,
    default: "",
  },
  label: {
    type: String,
    default: "",
  },
  //表单描述
  desc: {
    type: String,
    default: "",
  },
  margin: {
    type: Array as PropType<Array<number>>,
    default: () => [12, 12],
  },
  padding: {
    type: Array as PropType<Array<number>>,
    default: () => [0, 0],
  },
  //如果在forom绑定的model为深层对象，这里的名称需要如下:
  //比如model = {a:2,b:{c:333}}
  //如果想绑定c,则field = "b.c"
  field: {
    type: String,
    default: "",
  },
  //表彰底部的单项注意说明。
  help: {
    type: String,
    default: "",
  },
  //是否必填
  required: {
    type: Boolean,
    default: false,
  },
  //检验规则
  rules: {
    type: [Object, Array] as PropType<Array<rulesItem> | rulesItem>,
    default: () => {
      return [{ validator: false, required: false }];
    },
  },
  //显示下划线。
  border: {
    type: Boolean,
    default: null,
  },
  showError: {
    type: Boolean,
    default: true,
  },
  //校验不通过时，是否让标题跟着变化文字颜色，默认是。
  requiredTitleChangeColor: {
    type: Boolean,
    default: false,
  },
  transprent: {
    type: [Boolean, String],
    default: null,
  },
  round: {
    type: Number,
    default: 0,
  },
});
const item: Ref<formItem> = ref({
  label: "", //标签名称。
  field: props.field, //字段名称key.
  value: null,
  isRequiredError: false, //true,错误，false正常 检验状态
  message: "", //检验信息提示语。
  id: uni.$tm.u.getUid(1), //表单唯一标识id
  componentsName: "", //表单组件类型。
});
const _required = ref(props.required);
const tmFormLabelWidth = inject(
  "tmFormLabelWidth",
  computed(() => 100)
);
const tmFormLabelAlign = inject(
  "tmFormLabelAlign",
  computed(() => "left")
);
const tmFormLayout = inject(
  "tmFormLayout",
  computed(() => "horizontal")
);
const tmFormBorder_inject = inject(
  "tmFormBorder",
  computed(() => true)
);
const tmFormTransprent = inject(
  "tmFormTransprent",
  computed(() => false)
);

const tmFormBorder = computed(() => {
  if (props.border !== null && typeof props.border === "boolean") return props.border;
  return tmFormBorder_inject.value;
});
const _label = computed(() => props.label);
//父级方法。
let parent: any = proxy?.$parent;
while (parent) {
  if (parent?.tmFormComnameId == "tmFormId" || !parent) {
    break;
  } else {
    parent = parent?.$parent ?? undefined;
  }
}

//卸载后需要清空。
onUnmounted(() => {
  delCom();
});
provide(
  "tmFormItemRules",
  computed(() => {
    let defaultrs: Array<rulesItem> = [];
    if (Array.isArray(props?.rules)) {
      props?.rules.forEach((el) => {
        let isreq = el?.required || props.required;
        defaultrs.push({
          message: el?.message ?? "请填写必要的内容",
          required: isreq,
          validator: el?.validator ?? false,
        });
      });
    } else {
      defaultrs = [
        {
          message: props?.rules?.message ?? "请填写必要的内容",
          required: props.rules?.required || props.required,
          validator: props.rules?.validator ?? false,
        },
      ];
    }
    return defaultrs;
  })
);
const Rules = computed(() => {
  let defaultrs: Array<rulesItem> = [];
  if (Array.isArray(props?.rules)) {
    props?.rules.forEach((el) => {
      let isreq = el?.required || props.required;
      defaultrs.push({
        message: el?.message ?? "请填写必要的内容",
        required: isreq,
        validator: el?.validator ?? false,
      });
    });
  } else {
    defaultrs = [
      {
        message: props?.rules?.message ?? "请填写必要的内容",
        required: props.rules?.required || props.required,
        validator: props.rules?.validator ?? false,
      },
    ];
  }
  return defaultrs;
});
//向父级推表单类组件。
function pushCom(itemComval?: inputPushItem) {
  if (parent) {
    item.value = { ...item.value, ...(itemComval ?? {}) };
    parent.pushKey({ ...item.value });
  }
}
function delCom() {
  if (parent) {
    parent.delKey(item.value);
  }
}

const tmFormFun = inject(
  "tmFormFun",
  computed(() => "")
);
defineExpose({ pushCom, delCom, tmFormComnameFormItem });

// 验证优化如下:
/**
 * 从3.0.86开始,验证不与子组件挂勾,只验证绑定到form中的vmodel数据中的formitem中的filed字段.
 * 这样的改变意味着,传统的cell或者只用展示的view组件,只要提供了filed就能验证,无需特定Input,upload这样的
 * 组件才能进行验证.大大的提高了移动端的表单验证效率,特别适合大量的不同类型数据组件的验证规则.
 */
const formCallFiled: ComputedRef<any> = inject(
  "formCallFiled",
  computed(() => {})
);
const nowValue = computed(() => {
  return getObjectVal(formCallFiled.value, props.field);
});
let tid = NaN;
// let flatFormValue:any = {}
watch(
  () => nowValue.value,
  () => {
    if (_required.value) {
      clearTimeout(tid);
      tid = setTimeout(async () => {
        item.value = { ...(await checkVal()) };
        nextTick(() => {
          pushCom();
        });
      }, 300);
    }
  },
  { deep: true }
);

const validate = (rules: Array<rulesItem>, value: any) => {
  rules = rules.map((el) => {
    if (typeof el.validator === "function" && el.required === true) {
      return el;
    } else if (typeof el.validator === "boolean" && el.required === true) {
      return {
        ...el,
        validator: (val: any) => {
          if (val === null || val === "" || typeof val == "undefined") return false;
          if (typeof val === "object") {
            if (Array.isArray(val)) {
              if (val.length == 0) return false;
            } else if (Object.keys(val).length === 0 && val.constructor === Object) {
              return false;
            }
          }
          if (typeof val === "boolean") {
            return val;
          }
          if (typeof val === "number") {
            if (isNaN(val)) return false;
            if (Number(val) < 0) return false;
          }
          if (typeof val === "string") {
            if (val.trim().length == 0) return false;
          }
          return true;
        },
      };
    } else {
      return {
        ...el,
        validator: (val: string | number) => {
          return true;
        },
      };
    }
  });
  let rules_filter: Array<rulesItem> = rules.filter((el) => {
    return typeof el.validator === "function" && el.required === true;
  });
  let rules_fun: Array<Promise<rulesItem>> = rules_filter.map((el) => {
    return new Promise(async (res, rej) => {
      if (typeof el.validator === "function") {
        let vr = await el.validator(value);
        if (vr) {
          res({
            message: String(el.message),
            validator: true,
          });
        } else {
          rej({
            message: el.message,
            validator: false,
          });
        }
      } else {
        res({
          message: el.message,
          validator: true,
        });
      }
    });
  });

  return Promise.all(rules_fun);
};

function checkVal(): Promise<formItem> {
  return new Promise((resolve, reject) => {
    validate(Rules.value, getObjectVal(formCallFiled.value, props.field))
      .then((rulst) => {
        resolve({ ...item.value, isRequiredError: false });
      })
      .catch((err) => {
        resolve({ ...item.value, isRequiredError: !err.validator, message: err.message });
      });
  });
}

onMounted(() => {
  //预先推送。
  checkVal().then((elitem) => pushCom({ ...elitem }));
});

//--------------以下是专门为form表单专用------------------

watch(tmFormFun, () => {
  if (tmFormFun.value == "validate") {
    if (_required.value) {
      validate(Rules.value, getObjectVal(formCallFiled.value, props.field))
        .then((rulst) => {
          item.value.isRequiredError = false;
        })
        .catch((err) => {
          item.value.message = err.message;
          item.value.isRequiredError = !err.validator;
        });

      pushCom();
    }
  }
  if (tmFormFun.value == "reset") {
  }
  if (tmFormFun.value == "clearValidate") {
    item.value.isRequiredError = false;
  }
});

//-------------- end ------------------

/**通过扁平的字段来设置json的值. */
function setObjectVal(obj: any, field = "", val: any) {
  if (field == "") return obj;
  var arr = field.split(".");
  while (arr.length > 1) {
    let key = String(arr.shift());
    obj = isProxy(obj[key]) ? toRaw(obj[key]) : obj[key];
  }
  return (obj[arr[0]] = isProxy(val) ? toRaw(val) : val);
}
function getObjectVal(obj: any, field = "") {
  if (field == "") return obj;
  var arr = field.split(".");
  while (arr.length > 1) {
    let key = String(arr.shift());
    obj = toRaw(obj[key]);
  }
  return toRaw(obj[arr[0]]);
}

function parseJson2Form(prefix: string, JsonObject: any) {
  var realPrefix = prefix;
  var resObj: any = {};
  var gettype = Object.prototype.toString;
  for (var Key in JsonObject) {
    var tmpVal = JsonObject[Key];
    var typeStr = gettype.call(tmpVal);

    if (tmpVal == null || tmpVal == undefined) {
      resObj[realPrefix + "." + Key] = "";
    } else {
      if (
        typeStr == "[object String]" ||
        typeStr == "[object Number]" ||
        typeStr == "[object Boolean]" ||
        typeStr == "[object Null]" ||
        typeStr == "[object Undefined]"
      ) {
        resObj[realPrefix + "." + Key] = tmpVal + "";
      } else if (typeStr == "[object Object]") {
        _recursive_parseJson2Form_(resObj, realPrefix + "." + Key, tmpVal);
      } else if (typeStr == "[object Array]") {
        if (tmpVal.length <= 0) {
          resObj[realPrefix + "." + Key] = "";
        } else {
          var isSimpleArray = false;
          for (var arrIndex in tmpVal) {
            var childItem = tmpVal[arrIndex];
            var childTypeStr = gettype.call(childItem);
            if (
              childTypeStr == "[object String]" ||
              childTypeStr == "[object Number]" ||
              childTypeStr == "[object Boolean]" ||
              childTypeStr == "[object Null]" ||
              childTypeStr == "[object Undefined]"
            ) {
              isSimpleArray = true;
            }
          }
          if (isSimpleArray) {
            resObj[realPrefix + "." + Key] = tmpVal.join(",");
          } else {
            var nowIndex = 0;
            for (var arrIndex in tmpVal) {
              var childItem = tmpVal[arrIndex];
              _recursive_parseJson2Form_(
                resObj,
                realPrefix + "." + Key + "[" + nowIndex + "]",
                childItem
              );
              nowIndex++;
            }
          }
        }
      }
    }
  }

  return resObj;
}
function _recursive_parseJson2Form_(resObj: any, parentKey: string, JsonObject: any) {
  var gettype = Object.prototype.toString;
  for (var Key in JsonObject) {
    var tmpVal = JsonObject[Key];

    var typeStr = gettype.call(tmpVal);
    /*

        "[object String]";
        "[object Number]";
        "[object Boolean]"
        "[object Undefined]"
        "[object Null]"
        "[object Object]"
        "[object Array]"
        "[object Function]"
        */
    if (tmpVal == null || tmpVal == undefined) {
      resObj[parentKey + "." + Key] = "";
    } else {
      if (
        typeStr == "[object String]" ||
        typeStr == "[object Number]" ||
        typeStr == "[object Boolean]" ||
        typeStr == "[object Null]" ||
        typeStr == "[object Undefined]"
      ) {
        resObj[parentKey + "." + Key] = tmpVal + "";
      } else if (typeStr == "[object Object]") {
        _recursive_parseJson2Form_(resObj, parentKey + "." + Key, tmpVal);
      } else if (typeStr == "[object Array]") {
        if (tmpVal.length <= 0) {
          resObj[parentKey + "." + Key] = "";
        } else {
          var isSimpleArray = false;
          for (var arrIndex in tmpVal) {
            var childItem = tmpVal[arrIndex];
            var childTypeStr = gettype.call(childItem);

            if (
              childTypeStr == "[object String]" ||
              childTypeStr == "[object Number]" ||
              childTypeStr == "[object Boolean]" ||
              childTypeStr == "[object Null]" ||
              childTypeStr == "[object Undefined]"
            ) {
              isSimpleArray = true;
            }
          }
          if (isSimpleArray) {
            resObj[parentKey + "." + Key] = tmpVal.join(",");
          } else {
            var nowIndex = 0;
            for (var arrIndex in tmpVal) {
              var childItem = tmpVal[arrIndex];
              _recursive_parseJson2Form_(
                resObj,
                parentKey + "." + Key + "[" + nowIndex + "]",
                childItem
              );
              nowIndex++;
            }
          }
        }
      }
    }
  }
}
</script>

<style></style>
